<html>
<head>
<title>This tests the new EditContext APIs while in composition</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="test"></div>
<div id="test2"></div>
<script>

test(function() {
  const editContextDict = {
    text: "Hello world",
    selectionStart: 11,
    selectionEnd: 11,
    inputMode: "text",
    inputPanelPolicy: "auto",
    enterKeyHint: "enter"
  };
  const editContext = new EditContext(editContextDict);
  assert_not_equals(editContext, null);
  // Verify all the members of the EditContext
  assert_equals(editContext.text, "Hello world");
  assert_equals(editContext.selectionStart, 11);
  assert_equals(editContext.selectionEnd, 11);
  assert_equals(editContext.inputMode, "text");
  assert_equals(editContext.inputPanelPolicy, "auto");
  assert_equals(editContext.enterKeyHint, "enter");
}, 'Testing EditContext Dictionary Init');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  editContext.addEventListener("textupdate", e => {
    test.innerHTML = e.updateText;
  });
  test.editContext = editContext;
  test.focus();
  eventSender.keyDown('a');
  assert_equals(test.innerHTML, "a");
}, 'Testing EditContext English typing');

test(function() {
  const iframe = document.createElement("iframe");
  document.body.appendChild(iframe);
  const childDocument = iframe.contentDocument;
  const childDiv = childDocument.createElement('div');
  iframe.remove();
  childDiv.editContext = new EditContext();
}, 'Setting .editContext on a stray div should not crash.');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);

  const new_div = document.createElement("DIV");
  assert_equals(new_div.editContext, null);
  document.body.appendChild(new_div);

  new_div.editContext = editContext;
  assert_equals(new_div.editContext, editContext);
  new_div.editContext = null;
  assert_equals(new_div.editContext, null);
  document.body.removeChild(new_div);
}, 'Testing Element.editContext');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);

  const disconnected_div = document.createElement("DIV");
  assert_equals(disconnected_div.editContext, null);

  disconnected_div.editContext = editContext;
  assert_equals(disconnected_div.editContext, editContext);
  assert_equals(editContext.attachedElements().length, 1);
  assert_equals(editContext.attachedElements()[0], disconnected_div);
}, 'EditContext can be associated with an element that is not in the tree.');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);

  const div = document.createElement("DIV");
  assert_equals(div.editContext, null);

  document.body.appendChild(div);
  div.editContext = editContext;
  assert_equals(div.editContext, editContext);
  assert_equals(editContext.attachedElements().length, 1);
  assert_equals(editContext.attachedElements()[0], div);

  document.body.removeChild(div);
  assert_equals(div.editContext, editContext);
  assert_equals(editContext.attachedElements().length, 1);
  assert_equals(editContext.attachedElements()[0], div);
}, 'If an element is removed from the tree, the associated EditContext remains connected to the element.');

test(function() {
  const editContext = new EditContext();

  const div_parent = document.createElement("DIV");
  const div_child = document.createElement("DIV");
  document.body.appendChild(div_parent);
  div_parent.appendChild(div_child);

  div_child.editContext = editContext;
  assert_equals(div_child.editContext, editContext);
  assert_equals(div_parent.editContext, null);
  assert_equals(editContext.attachedElements().length, 1);
  assert_equals(editContext.attachedElements()[0], div_child);

  document.body.removeChild(div_parent);
  assert_equals(div_child.editContext, editContext);
  assert_equals(editContext.attachedElements().length, 1);
  assert_equals(editContext.attachedElements()[0], div_child);
}, 'If an element\'s ancestor is removed from tree, the associated EditContext remains connected to the element.');

test(function() {
  const editContext = new EditContext();

  const test = document.getElementById("test");
  const test2 = document.getElementById("test2");

  test.editContext = editContext;
  test2.editContext = editContext;

  assert_equals(test.editContext, editContext);
  assert_equals(test2.editContext, editContext);
  assert_equals(editContext.attachedElements().length, 2);
  assert_equals(editContext.attachedElements()[0], test);
  assert_equals(editContext.attachedElements()[1], test2);

  test.editContext = null;

  assert_equals(editContext.attachedElements().length, 1);
  assert_equals(editContext.attachedElements()[0], test2);
}, '.attachedElements() should return associated elements');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "");
  eventSender.keyDown('a');
  assert_equals(test.innerHTML, "");
}, 'EditContext should disable DOM mutation');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase() + buffer.substr(e.updateRangeEnd);
  });
  test.focus();
  test.editContext = editContext;
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
}, 'Testing attaching EditContext AFTER the element is focused.');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase() + buffer.substr(e.updateRangeEnd);
  });
  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.setComposition("bar");
  assert_equals(test.innerHTML, "BAR");
}, 'Testing EditContext TextUpdate');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.focus();
  test.editContext = editContext;
  test.addEventListener("beforeinput", e => {
    if (e.inputType === "insertText") {
      e.preventDefault();
    }
  });
  eventSender.keyDown('a');
  assert_equals(editContext.text, "");
}, 'beforeInput(insertText) should be cancelable');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase() + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.setComposition("");
  assert_equals(test.innerHTML, "");
}, 'Testing EditContext TextUpdate with empty strings');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  let textFormats = [];
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });
  editContext.addEventListener("textformatupdate", e => {
    //TODO: Currently Chromium only fires default styles
    textFormats = e.getTextFormats();
  });
  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  assert_equals(textFormats.length, 1);
  assert_equals(textFormats[0].rangeStart, 0);
  assert_equals(textFormats[0].rangeEnd, 3);
  assert_equals(textFormats[0].underlineColor, "rgba(0, 0, 0, 0)");
  assert_equals(textFormats[0].backgroundColor, "rgba(0, 0, 0, 0)");
  assert_equals(textFormats[0].textColor, "rgba(0, 0, 0, 0)");
  assert_equals(textFormats[0].underlineStyle, "Solid");
  assert_equals(textFormats[0].underlineThickness, "Thin");
}, 'Testing EditContext TextFormatUpdate');

test(function() {
  const editContext1 = new EditContext();
  const editContext2 = new EditContext();
  assert_not_equals(editContext1, null);
  assert_not_equals(editContext2, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext1.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });
  editContext2.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toLowerCase()  + buffer.substr(e.updateRangeEnd);
  });
  test.editContext = editContext1;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.setComposition("bar");
  assert_equals(test.innerHTML, "BAR");
  test.innerHTML = "";
  test.editContext = editContext2;
  textInputController.setComposition("HELLO");
  assert_equals(test.innerHTML, "hello");
  textInputController.setComposition("WORLD");
  assert_equals(test.innerHTML, "world");
}, 'Testing Multiple EditContext TextUpdates');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  var compositionStartFired = 0;
  var compositionEndFired = 0;
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });
  editContext.addEventListener("compositionstart", e => {
    // Update the text in the div
    compositionStartFired++;
  });
  editContext.addEventListener("compositionend", e => {
    compositionEndFired++;
  });
  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  assert_equals(compositionStartFired, 1);
  textInputController.setComposition("bar");
  assert_equals(test.innerHTML, "BAR");
  assert_equals(compositionStartFired, 1);
  textInputController.insertText("bar");
  assert_equals(test.innerHTML, "BAR");
  assert_equals(compositionEndFired, 1);
}, 'Testing EditContext Composition Event');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("f");
  assert_equals(test.innerHTML, "F");
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.insertText("foobar");
  assert_equals(test.innerHTML, "FOOBAR");
}, 'Testing EditContext Text updates');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("f");
  assert_equals(test.innerHTML, "F");
  textInputController.setComposition("");
  assert_equals(test.innerHTML, "");
  textInputController.insertText("foobar");
  assert_equals(test.innerHTML, "FOOBAR");
}, 'Testing EditContext Text updates with empty text');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  editContext.updateText(0, 1, "h");
  assert_equals(editContext.text, "hoo");
  editContext.updateText(0, 3, "bar");
  assert_equals(editContext.text, "bar");
}, 'Testing EditContext TextChange');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.insertText("bar");
  assert_equals(editContext.text, "bar");
  assert_equals(editContext.selectionStart, 3);
  assert_equals(editContext.selectionEnd, 3);
  editContext.updateSelection(0, 0);
  assert_equals(editContext.selectionStart, 0);
  assert_equals(editContext.selectionEnd, 0);
  textInputController.setComposition("foo");
  assert_equals(editContext.text, "foobar");
  textInputController.insertText("foo");
  assert_equals(editContext.selectionStart, 3);
  assert_equals(editContext.selectionEnd, 3);
}, 'Testing EditContext Selection Change');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  editContext.updateText(0, 1, "h");
  assert_equals(editContext.text, "hoo");
  editContext.updateText(0, 3, "bar");
  assert_equals(editContext.text, "bar");
  textInputController.insertText("bar");
  assert_equals(editContext.text, "bar");
  editContext.updateSelection(0, 0);
  assert_equals(editContext.selectionStart, 0);
  assert_equals(editContext.selectionEnd, 0);
  textInputController.setComposition("foo");
  assert_equals(editContext.text, "foobar");
  textInputController.insertText("foo");
  assert_equals(editContext.text, "foobar");
}, 'Testing EditContext Text And Selection Change');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.insertText("foo");
  textInputController.setMarkedTextFromExistingText(0, 3);
  textInputController.insertText("bar");
  assert_equals(editContext.text, "bar");
  textInputController.setMarkedTextFromExistingText(2, 3);
  textInputController.insertText("t");
  assert_equals(editContext.text, "bat");
}, 'Testing EditContext SetCompositionFromExistingText');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.insertText("foo");
  assert_equals(editContext.selectionStart, 3);
  assert_equals(editContext.selectionEnd, 3);
  textInputController.extendSelectionAndDelete(3, 0);
  assert_equals(editContext.selectionStart, 0);
  assert_equals(editContext.selectionEnd, 0);
  textInputController.insertText("bar");
  assert_equals(editContext.text, "bar");
  assert_equals(editContext.selectionStart, 3);
  assert_equals(editContext.selectionEnd, 3);
  textInputController.extendSelectionAndDelete(1, 0);
  assert_equals(editContext.selectionStart, 2);
  assert_equals(editContext.selectionEnd, 2);
  assert_equals(editContext.text, "ba");
  textInputController.insertText("t");
  assert_equals(editContext.text, "bat");
}, 'Testing EditContext ExtendSelectionAndDelete');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  textInputController.unmarkText();
  assert_equals(editContext.text, "foo");
  textInputController.setComposition("bar");
  assert_equals(editContext.text, "foobar");
}, 'Testing EditContext FinishComposingText');

test(function() {
  const editContext1 = new EditContext();
  assert_not_equals(editContext1, null);
  const editContext2 = new EditContext();
  assert_not_equals(editContext2, null);
  const test = document.getElementById('test');
  test.innerHTML = "";
  // Add EditContext event listeners
  editContext1.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toUpperCase()  + buffer.substr(e.updateRangeEnd);
  });

  editContext2.addEventListener("textupdate", e => {
    // Update the text in the div
    const buffer = test.innerText;
    test.innerHTML = buffer.substr(0, e.updateRangeStart) + e.updateText.toLowerCase()  + buffer.substr(e.updateRangeEnd);
  });

  test.editContext = editContext1;
  test.focus();
  textInputController.setComposition("foo");
  assert_equals(test.innerHTML, "FOO");
  test.blur();
  textInputController.setComposition("foo2");
  assert_equals(test.innerHTML, "FOO");

  const test2 = document.getElementById('test2');
  test2.editContext = editContext2;
  test2.focus();
  textInputController.setComposition("BAR");
  assert_equals(editContext2.text, "BAR");
  assert_equals(test.innerHTML, "barFOO");
  assert_equals(editContext1.text, "foo");
}, 'Testing EditContext blur');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  editContext.text = "foo";
  assert_equals(editContext.text, "foo");
  const test = document.getElementById('test');
  // Update the layout of the |EditContext|
  var viewRect = test.getBoundingClientRect();
  viewRect.x = viewRect.left;
  viewRect.y = viewRect.top;
  var caretRect = test.getBoundingClientRect();
  caretRect.x = caretRect.left;
  caretRect.y = 2.2 * caretRect.top;
  caretRect.width = 1;
  editContext.updateSelection(0, 0);
  assert_equals(editContext.selectionStart, 0);
  assert_equals(editContext.selectionEnd, 0);
  editContext.selectionStart = 1;
  assert_equals(editContext.selectionStart, 0); // selectionEnd is still 0
  editContext.selectionEnd = 1;
  assert_equals(editContext.selectionEnd, 1);
  editContext.selectionStart = 1;
  assert_equals(editContext.selectionStart, 1);
  editContext.updateControlBounds(viewRect);
  editContext.updateSelectionBounds(caretRect);
}, 'Testing EditContext update text, selection and layout');

test(function() {
  const editContext = new EditContext();
  const test = document.getElementById('test');
  var rect1 = DOMRect.fromRect({x:0, y:1, width:100, height:200});
  var rect2 = DOMRect.fromRect({x:2, y:3, width:300, height:400});
  var rectArray = [rect1, rect2];
  var rangeStart = 2;
  editContext.updateCharacterBounds(rangeStart, rectArray);
  assert_equals(editContext.characterBoundsRangeStart, 2);

  var actualRectArray = editContext.characterBounds();
  assert_equals(actualRectArray.length, 2);
  assert_equals(actualRectArray[0].x, 0);
  assert_equals(actualRectArray[0].y, 1);
  assert_equals(actualRectArray[0].width, 100);
  assert_equals(actualRectArray[0].height, 200);
  rect2.x=100;
  assert_equals(actualRectArray[1].x, 2);  // the cached value shouldn't change.
  assert_equals(actualRectArray[1].y, 3);
  assert_equals(actualRectArray[1].width, 300);
  assert_equals(actualRectArray[1].height, 400);
}, 'updateCharacterBounds(), characterBounds(), and characterBoundsRangeStart should work properly');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  editContext.text = "foo";
  assert_equals(editContext.text, "foo");
  assert_throws_dom("IndexSizeError", function() { editContext.updateSelection(10, 0); });
  assert_equals(editContext.selectionStart, 0);
  assert_equals(editContext.selectionEnd, 0);
  assert_throws_dom("IndexSizeError", function() { editContext.updateText(10, 1, "h"); });
  assert_equals(editContext.text, "foo");
}, 'Testing EditContext update text and selection with invalid values');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  assert_equals(editContext.inputMode, "text"); // default value
  editContext.inputMode = "tel";
  assert_equals(editContext.inputMode, "tel");
  editContext.inputMode = "email";
  assert_equals(editContext.inputMode, "email");
  editContext.inputMode = "search";
  assert_equals(editContext.inputMode, "search");
  editContext.inputMode = "decimal";
  assert_equals(editContext.inputMode, "decimal");
  editContext.inputMode = "numeric";
  assert_equals(editContext.inputMode, "numeric");
  editContext.inputMode = "url";
  assert_equals(editContext.inputMode, "url");
  editContext.inputMode = "none";
  assert_equals(editContext.inputMode, "none");
  editContext.inputMode = "test";
  assert_equals(editContext.inputMode, "none");
}, 'Testing EditContext inputMode');

test(function() {
  const editContext = new EditContext();
  assert_not_equals(editContext, null);
  assert_equals(editContext.enterKeyHint, "enter"); // default value
  editContext.enterKeyHint = "done";
  assert_equals(editContext.enterKeyHint, "done");
  editContext.enterKeyHint = "go";
  assert_equals(editContext.enterKeyHint, "go");
  editContext.enterKeyHint = "next";
  assert_equals(editContext.enterKeyHint, "next");
  editContext.enterKeyHint = "previous";
  assert_equals(editContext.enterKeyHint, "previous");
  editContext.enterKeyHint = "search";
  assert_equals(editContext.enterKeyHint, "search");
  editContext.enterKeyHint = "send";
  assert_equals(editContext.enterKeyHint, "send");
  editContext.enterKeyHint = "test";
  assert_equals(editContext.enterKeyHint, "send");
}, 'Testing EditContext enterKeyHint');

test(function() {
  // SetComposition should not crash when event handler removes document
  const child = document.createElement("iframe");
  document.body.appendChild(child);
  const childDocument = child.contentDocument;
  const textarea = childDocument.createElement('textarea');
  childDocument.body.appendChild(textarea);
  textarea.addEventListener("focusin", e => {
    const childEditContext = new EditContext();
    textarea.editContext = childEditContext;
    childEditContext.addEventListener("textupdate", e => {
      child.remove();
    });
    childEditContext.addEventListener("textformatupdate", e => {
    });
  });
  textarea.focus();
  child.contentWindow.focus();
  child.contentWindow.textInputController.setComposition("bar");
}, 'Testing EditContext Iframe Document Delete');

test(function() {
  const editContext1 = new EditContext();
  editContext1.addEventListener("textupdate", e => {
  });
  const test = document.getElementById('test');
  test.editContext = editContext1;
  test.focus();
  gc()
  textInputController.setComposition("bar");

}, 'Testing EditContext GC');

</script>
</body>
</html>
